/**
 * \file Type.h
 * \brief Headers for Type and its subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __TYPE_H_INCLUDED
#define __TYPE_H_INCLUDED

#include <vector>
#include "CStr.h"

#include <typeinfo>

class Value;
class TypeSymbol;

class Type
{
  public:
    Type(TypeSymbol *_sym=NULL) : sym(_sym) {}
    virtual ~Type() {}

    virtual Type* copy() = 0;

    virtual bool equals(Type *rhs)
    {
      return typeid(*this) == typeid(*rhs);
    }  

    std::string toString(int indentation=0) { return _toString(false, indentation); }
    virtual std::string _toString(bool useTypeDefs, int indentation=0) = 0;
    virtual std::string toPreString(bool useTypeDefs, int indentation=0)
    {
      return _toString(useTypeDefs, indentation);
    }
    virtual std::string toPostString(int indentation=0) { return ""; }
    virtual std::string toTerm() = 0;
    virtual Value* makeValue() = 0;
    virtual Value* makeValue(double _data) = 0;

    virtual int size() = 0;
  public:
    TypeSymbol *sym;
};
    
class UnknownType : public Type
{
  public:
    UnknownType* copy()
    {
      return new UnknownType();
    }

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);
    int size() { return 0; }
};

extern UnknownType Unknown;

class VoidType : public Type
{
  public:
    VoidType(TypeSymbol *_sym=NULL) : Type(_sym) { }

    VoidType* copy()
    {
      return new VoidType(sym);
    }

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);

    int size() { return 0; }
};

extern VoidType BasicVoid;
  
class IntegerType : public Type
{
  public:
    IntegerType(TypeSymbol *_sym=NULL, int _precision=31) :
      Type(_sym), precision(_precision) { }

    IntegerType* copy()
    {
      return new IntegerType(sym, precision);
    }

    bool equals(Type *rhs);

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);

    int size() { return (precision+1)/8; }

  public:
    int precision;
};

extern IntegerType BasicChar;
extern IntegerType BasicUChar;
extern IntegerType BasicShort;
extern IntegerType BasicUShort;
extern IntegerType BasicInteger;
extern IntegerType BasicUInteger;

class FloatType : public Type
{
  public:
    FloatType(TypeSymbol *_sym=NULL, int _precision=32) :
      Type(_sym), precision(_precision) { }

    FloatType* copy()
    {
      return new FloatType(sym, precision);
    }

    bool equals(Type *rhs);

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);

    int size() { return (precision+1)/8; }

  public:
    int precision;
};

extern FloatType BasicFloat;
extern FloatType BasicUFloat;
extern FloatType BasicDouble;
extern FloatType BasicUDouble;

class PointerOf;

class PointerType : public Type
{
  public:
    PointerType(Type *_to, int _elements=0);
    PointerType(TypeSymbol *_sym=NULL, Type *_to=&BasicVoid, int _elements=0, PointerOf *_deref=NULL);
    ~PointerType();

    PointerType* copy();
    bool equals(Type *rhs);

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toPreString(bool useTypeDefs, int indentation=0);
    std::string toPostString(int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);

    int size();

  public:
    Type *to;

    /*** Number of elements, if this is an array pointer. There are three separate cases:
     * \li -1  This is a variable-length array
     * \li  0  This is not an array, but a normal pointer
     * \li >0  A normal array
     */
    int elements;

    PointerOf *deref;
};

extern PointerType NullPointer;

class CodeType : public Type
{
  public:
    CodeType(TypeSymbol *_sym=NULL) : Type(_sym) { }

    CodeType* copy()
    {
      return new CodeType();
    }

    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);

    int size() { return 0; }
};

extern CodeType BasicCode;

class SubType
{
  public:
    SubType(std::string _name="", Type *_type=&BasicVoid) : name(_name), type(_type) {}

    SubType *copy()
    {
      return new SubType(name, type);
    }

  public:
    std::string name;
    Type *type;
};
      
class StructType : public Type
{
  public:
    typedef std::vector<SubType*>::iterator SI;

    StructType(TypeSymbol *_sym=NULL) : Type(_sym) { }

    StructType* copy()
    {
      StructType *res = new StructType;

      if (!subtypes.empty())
	for (SI ii=subtypes.begin(); ii != subtypes.end(); ++ii)
	  res->push_back((*ii)->copy());

      return res;
    }

    bool equals(Type *rhs);

    void push_back(SubType *type);
    std::string _toString(bool useTypeDefs, int indentation=0);
    std::string toTerm();
    Value* makeValue();
    Value* makeValue(double _data);
    int size();

    int findMemberIndex(std::string name);
  public:
    std::vector<SubType*> subtypes;
};

class SymbolTable;
class IntegerValue;

Type *constructType(Type *base, std::vector<PointerOf*> derefs, SymbolTable *symbols=NULL);
Type *constructType(Type *base, std::vector<IntegerValue*>&bounds,
		    SymbolTable *symbols=NULL);

#endif /* __TYPE_H_INCLUDED */
